<template>
  <div class="filepreview-container">
    <div class="file-list">
      <div
        v-for="(item, index) in fileList"
        :key="index"
        class="file-item"
        @click="handleShowFile(item)"
      >{{ item.src }}</div>
    </div>
    <div v-if="showFile" class="file-preview-dialog" @click="handleClose">
      <div class="file-preview-content" @click.stop="()=>{}">
        <div class="file-box">
          <div
            v-if="!supportedTypes.includes(extractFileType(targetFileSrc))"
            class="empty-box"
          >在线预览服务当前仅支持docx/xlsx/xls/pdf/jpg/jpeg/png/txt/mp4/mp3</div>
          <div
            v-else-if="extractFileType(targetFileSrc) === 'xls'"
            class="file-content xls-content"
          >
            <div id="tabs-container">
              <div class="tabs-header">
                <!-- 动态生成标签页 -->
                <div
                  :class="['tab-item', { active: activeSheetName === itemSheetName }]"
                  v-for="(item, itemSheetName) in xlsData"
                  :key="itemSheetName"
                  @click="activeSheetName = itemSheetName"
                >{{ itemSheetName }}</div>
              </div>
              <div class="tabs-content">
                <!-- 显示当前选中的表格 -->
                <template v-if="activeSheetName && xlsData[activeSheetName]">
                  <table v-if="xlsData[activeSheetName].length > 0" class="data-table">
                    <thead>
                      <tr>
                        <!-- 表头 -->
                        <th v-for="(item, key) in xlsData[activeSheetName][0]" :key="key">{{ key }}</th>
                      </tr>
                    </thead>
                    <tbody>
                      <!-- 表格内容 -->
                      <tr v-for="row in xlsData[activeSheetName]" :key="row.id">
                        <td v-for="(cell, key) in row" :key="key">{{ cell }}</td>
                      </tr>
                    </tbody>
                  </table>
                  <div v-else>当前Sheet页无数据</div>
                </template>
                <div v-else>请选择一个Sheet页</div>
              </div>
            </div>
          </div>
          <VueOfficeDocx
            v-else-if="extractFileType(targetFileSrc) === 'docx'"
            :src="targetFileSrc"
            class="file-content docx-content"
          />
          <VueOfficeExcel
            v-else-if="extractFileType(targetFileSrc) === 'xlsx'"
            :src="targetFileSrc"
            class="file-content xlsx-content"
          />
          <VueOfficePdf
            v-else-if="extractFileType(targetFileSrc) === 'pdf'"
            :src="targetFileSrc"
            class="file-content pdf-content"
          />
          <div
            v-else-if="['jpeg', 'png', 'jpg'].includes(extractFileType(targetFileSrc))"
            class="file-content img-content"
          >
            <img :src="targetFileSrc" :alt="targetFileSrc" />
          </div>
          <div
            v-else-if="extractFileType(targetFileSrc) === 'txt'"
            class="file-content txt-content"
          >
            <pre><code>{{ txtData }}</code></pre>
          </div>
          <div
            v-else-if="extractFileType(targetFileSrc) === 'mp3'"
            class="file-content audio-content"
          >
            <!-- 音频文件 -->
            <audio :src="targetFileSrc" controls width="100%" height="30px">您的浏览器不支持 audio 标签。</audio>
          </div>
          <div
            v-else-if="extractFileType(targetFileSrc) === 'mp4'"
            class="file-content video-content"
          >
            <!-- 视频文件 -->
            <video controls width="auto" height="100%">
              <source :src="targetFileSrc" type="video/mp4" />您的浏览器不支持 Video 标签。
            </video>
          </div>
        </div>
        <div class="file-box-btns">
          <div @click="downLoadFile">下载</div>
          <div @click="handleClose">取消</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup name="FilePreview">
import { ref, computed, defineProps } from "vue"
import { getToken } from "@/utils/auth"
import VueOfficeDocx from "@vue-office/docx"
import "@vue-office/docx/lib/index.css"
import VueOfficeExcel from "@vue-office/excel"
import "@vue-office/excel/lib/index.css"
import VueOfficePdf from "@vue-office/pdf"
import * as XLSX from "xlsx-js-style"
// 支持的文件类型数组
const supportedTypes = ["docx", "xlsx", "xls", "pdf", "jpg", "jpeg", "png", "txt", "mp4", "mp3"]

// 封装一个fetch方法，通过文件路径获取二进制文件内容
const getFileArrayBufferBySrc = async (src, useLocalToken = false) => {
  const headers = new Headers()
  if (useLocalToken) {
    headers.set("Authorization", `Bearer ${getToken()}`)
  }

  const res = await fetch(src, { headers })
  if (!res.ok) {
    throw new Error("Network response was not ok")
  }
  return await res.arrayBuffer()
}

// 提取文件类型函数，通过文件路径获取文件扩展名
const extractFileType = path => {
  const dotIndex = path.lastIndexOf(".")
  return dotIndex !== -1 ? path.substring(dotIndex + 1) : "unknown"
}

// 验证URL是否有效的函数
const isValidUrl = url => {
  const pattern = new RegExp(
    "^(https?:\\/\\/)?" + // 协议
      "((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // 域名
      "((\\d{1,3}\\.){3}\\d{1,3}))" + // IP
      "(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // 端口和路径
      "(\\?[;&a-z\\d%_.~+=-]*)?" + // 查询字符串
      "(\\#[-a-z\\d_]*)?$",
    "i"
  ) // 锚点
  return !!pattern.test(url)
}

// 定义组件属性
// list可以是字符串数组，也可以是object数组[{name: '', src: ''}]
const props = defineProps({
  list: {
    type: Array,
    default: () => [
      "https://kqtest.cloudsid.cn:9760/api/profile/upload/2024/09/29/新建 Microsoft Word 文档_20240929150727A008.doc",
      "https://kqtest.cloudsid.cn:9760/api/profile/upload/2024/09/29/自动驾驶_20240929150439A004.docx",
      "https://kqtest.cloudsid.cn:9760/api/profile/upload/2024/09/29/2024-05请假记录_20240929150623A006.xls",
      "https://kqtest.cloudsid.cn:9760/api/profile/upload/2024/09/30/2024-05请假记录_20240930095255A012.xls",
      "https://kqtest.cloudsid.cn:9760/api/profile/upload/2024/09/29/2024-05请假记录xlsx_20240929150652A007.xlsx",
      "https://kqtest.cloudsid.cn:9760/api/profile/upload/2024/09/29/test_20240929150507A005.pdf",
      "https://kqtest.cloudsid.cn:9760/api/profile/upload/2024/09/29/a6676f60ba725057a384bcfd0787a156_20240929150811A009.jpeg",
      "https://kqtest.cloudsid.cn:9760/api/profile/upload/2024/09/30/新建 文本文档_20240930105836A015.txt",
      "https://kqtest.cloudsid.cn:9760/api/profile/upload/2024/09/30/mda-meb3dhpmz0xpqh8w_20240930110520A016.mp4",
      "https://kqtest.cloudsid.cn:9172/api/file/getFile/1727666801194.mp3"
    ]
  }
})

// 文件预览对话框显示状态
const showFile = ref(false)
// 当前预览文件的源路径
const targetFileSrc = ref("")
// 当前活动的Sheet名称，用于Excel文件预览
const activeSheetName = ref(undefined)
// Excel文件数据
const xlsData = ref({})
// 文本文件数据
const txtData = ref(undefined)

// 处理文件列表，根据URL或对象生成统一的文件列表格式
const fileList = computed(() => {
  return props.list.map(item => {
    if (typeof item === "string") {
      const lastSlashIndex = item.lastIndexOf("/")
      const dotIndex = item.lastIndexOf(".")
      if (lastSlashIndex !== -1 && dotIndex !== -1 && lastSlashIndex < dotIndex) {
        const fileName = item.substring(lastSlashIndex + 1, dotIndex)
        const fileType = item.substring(dotIndex + 1)
        return { name: fileName, src: item, filetype: fileType }
      } else {
        return { name: "未知文件类型", src: item, filetype: "unknown" }
      }
    } else if (typeof item === "object" && item !== null && "src" in item) {
      const fileName = "name" in item ? item.name : "unknown"
      const fileType = extractFileType(item.src)
      return { name: fileName, src: item.src, filetype: fileType }
    } else {
      return { name: "unknown", src: "unknown", filetype: "unknown" }
    }
  })
})

// 处理文件预览
const handleShowFile = async item => {
  showFile.value = true
  targetFileSrc.value = item.src

  if (item.filetype === "xls") {
    const arrayBuffer = await getFileArrayBufferBySrc(item.src, false)
    const data = new Uint8Array(arrayBuffer)
    const workbook = XLSX.read(data, { type: "buffer" })
    activeSheetName.value = workbook.SheetNames[0]
    workbook.SheetNames.forEach(sheetName => {
      const worksheet = workbook.Sheets[sheetName]
      xlsData.value[sheetName] = XLSX.utils.sheet_to_json(worksheet)
    })
  } else if (item.filetype === "txt") {
    const arrayBuffer = await getFileArrayBufferBySrc(item.src, false)
    const decoder = new TextDecoder("utf-8")
    txtData.value = decoder.decode(arrayBuffer)
  }
}

// 处理文件下载
const downLoadFile = () => {
  if (!isValidUrl(targetFileSrc.value)) {
    console.error("下载路径不正确.")
    return
  }

  fetch(targetFileSrc.value, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${getToken()}`,
      "Content-Type": "application/x-www-form-urlencoded"
    }
  })
    .then(response => {
      if (!response.ok) {
        throw new Error("网络连接失败")
      }
      return response.blob() // 将 Response 转换为 Blob
    })
    .then(data => {
      const reader = new FileReader()
      // 将 Blob 读取为 Data URL
      reader.readAsDataURL(data)
      // 读取 blob 数据为 Data URL
      reader.onloadend = function () {
        const a = document.createElement("a")
        a.href = reader.result // 结果为 Base64 编码的数据
        a.target = "_blank" // 设置为'_blank'以在新窗口或标签页中打开
        a.rel = "noopenner noreferrer"
        const lastSlashIndex = targetFileSrc.value.lastIndexOf("/")
        const fileName = targetFileSrc.value.substring(lastSlashIndex + 1)
        a.download = fileName // 设置文件名
        a.click() // 触发下载
        handleClose()
      }
    })
    .catch(error => {
      console.error("下载失败:", error)
    })
}

// 关闭文件预览对话框
const handleClose = () => {
  showFile.value = false
  targetFileSrc.value = undefined
  activeSheetName.value = undefined
  xlsData.value = {}
  txtData.value = undefined
}
</script>

<style lang="scss" scoped>
$border-color: #e4e7ed;
$hover-bg: #f5f7fa;
$hover-color: #69b4ff;
$active-color: #409eff;

$file-preview-dialog-width: 100vw;
$file-preview-dialog-height: 100vh;

.filepreview-container {
  width: 100%;
  max-width: 1000px;
  overflow: auto;
  .file-list {
    .file-item {
      border: 1px solid $border-color;
      line-height: 2;
      margin-bottom: 10px;
      position: relative;
      // 省略号
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
      padding: 0 10px;
      // hover
      transition: background-color 0.3s, color 0.3s;
      &:hover {
        background-color: $hover-bg;
        cursor: pointer;
        color: $hover-color;
      }
    }
  }
  .file-preview-dialog {
    width: $file-preview-dialog-width;
    height: $file-preview-dialog-height;
    background-color: rgba(0, 0, 0, 0.4);
    overflow: hidden;
    position: fixed;
    left: 0;
    top: 0;
    z-index: 999;
    display: flex;
    justify-content: center;
    align-items: center;
    .file-preview-content {
      width: 80vw;
      height: 90vh;
      background-color: #fff;
      border-radius: 5px;
      padding: 30px;
      display: flex;
      flex-flow: column;
      .file-box {
        flex: 1;
        height: calc(90vh - 80px - 60px);
        .file-content {
          width: 100%;
          height: 100%;
          display: flex;
          flex-flow: column;
          overflow: auto;
          background-color: #808080;
        }
        .xls-content {
          // 表格table的基础样式
          #tabs-container {
            width: 100%;
            height: 100%;
            background-color: #fff;
            display: flex;
            flex-direction: column;
            border: 1px solid $border-color;
            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
            .tabs-header {
              width: 100%;
              display: flex;
              gap: 5px;
              background-color: #f2f2f2;
              padding: 5px 5px 0;
              .tab-item {
                padding: 8px 16px;
                cursor: pointer;
                border: 1px solid $border-color;
                border-bottom: none;
                color: #606266;
                font-size: 14px;
                line-height: 1.5;
                transition: all 0.3s;
                &.active {
                  background-color: white;
                  position: relative;
                  top: -1px; /* 下沉效果 */
                  border-bottom-color: transparent;
                  color: $active-color;
                  font-weight: bold;
                }
              }
            }
            .tabs-content {
              width: 100%;
              height: calc(100% - 36px); /* 减去头部高度 */
              padding: 10px;
              overflow: auto;
              border: 1px solid $border-color;
              border-top: none;
              .data-table {
                width: 100%;
                border-collapse: collapse;
                overflow: auto;
                font-size: 14px;
                color: #606266;
                th,
                td {
                  border: 1px solid $border-color;
                  padding: 8px;
                  text-align: left;
                  min-width: 100px;
                  min-height: 30px;
                  background-color: #fff;
                  vertical-align: middle;
                }
                th {
                  background-color: #f2f2f2;
                  color: #606266;
                  font-weight: bold;
                }
                tr:hover {
                  background-color: $hover-bg;
                }
              }
            }
          }
        }
        .img-content {
          > img {
            width: 100%;
            height: auto;
          }
        }
        .txt-content {
          font-size: 15px;
          line-height: 1.5;
          background-color: #fff;
        }
        .audio-content {
          display: flex;
          justify-content: center;
          align-items: center;
        }
        .empty-box {
          color: #aaaaaa;
          fill: currentColor;
          width: 100%;
          height: 100%;
          vertical-align: top;
          display: flex;
          flex-direction: column;
          justify-content: center;
          align-items: center;
        }
      }
      .file-box-btns {
        display: flex;
        flex-flow: row;
        justify-content: center;
        margin: 20px 0;
        > div {
          margin: 0 20px;
          background-color: $active-color;
          color: #fff;
          line-height: 40px;
          padding: 0 20px;
          border-radius: 4px;
          cursor: pointer;
          transition: background-color 0.3s;
          &:hover {
            background-color: lighten($active-color, 10%);
            color: #fff;
          }
        }
      }
    }
  }
}

// 响应式设计
@media (max-width: 768px) {
  .filepreview-container {
    max-width: 100%;
  }
  .file-preview-dialog {
    width: 100%;
    height: 100%;
    .file-preview-content {
      width: 90%;
      height: 80%;
      padding: 20px;
      .file-box {
        height: calc(80% - 40px - 40px);
        .file-content {
          height: 100%;
        }
      }
    }
  }
}
</style>